package bg.btanev.eightyfour.domain.profile.feed.processor;

import java.util.Collection;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import bg.btanev.eightyfour.domain.entitybase.Domain;

import com.sun.syndication.feed.module.Module;
import com.sun.syndication.feed.synd.SyndEntry;

/**
 *
 * @author B.Tanev
 *
 * @param <D>
 *            {@link Module} processing result {@link Domain} type
 * @param <M>
 *            {@link Module} type
 */
public abstract class BaseRssModulesProcessor<D extends Domain, M extends Module> {
	private final SyndEntry feedEntry;

	private M rssModule;
	private boolean rssModuleInit = false;
	private final Object rssModuleLocker = new Object();

	BaseRssModulesProcessor(final SyndEntry feedEntry) {
		Validate.notNull(feedEntry, "Feed entry cannot be null.");

		this.feedEntry = feedEntry;
	}

	/**
	 *
	 * Tries to find and initialize a specified RSS {@link Module}. This method <strong>must be invoked before</strong>
	 * {@link #processModule()} method.
	 *
	 * @return <code>true</code> if the specified RSS {@link Module} was found and initialized otherwise the result is
	 *         <code>false</code>.
	 */
	@SuppressWarnings("unchecked")
	public final boolean hasRssModule() {
		if (!rssModuleInit) {
			String[] moduleNamespaces = getModuleNamespaces();
			assert (!ArrayUtils.isEmpty(moduleNamespaces));

			synchronized (rssModuleLocker) {
				if (!rssModuleInit) {
					for (String namespace : moduleNamespaces) {
						assert (!StringUtils.isEmpty(namespace));

						if ((this.rssModule = (M) this.feedEntry.getModule(namespace)) != null) {
							break;
						}
					}

					rssModuleInit = true;
				}
			}
		}

		return this.rssModule != null;
	}

	/**
	 *
	 * @return a {@link Collection} of {@link Persistable} object in a result of processing the specified RSS
	 *         {@link Module}.
	 *
	 * @throws IllegalStateException
	 *             when the internal RSS {@link Module} has not initialized. The internal RSS {@link Module} state is
	 *             initialized by invoking the {@link #hasRssModule()} method. For the module processor to be in correct
	 *             state the {@link #hasRssModule()} must return value <code>true</code>.
	 */
	public final Collection<D> processModule() {
		if (this.rssModule == null) {
			throw new IllegalStateException("Rss module not initialize. Call hasRssModule() method first.");
		}

		return processModuleInternal(this.rssModule);
	}

	/**
	 *
	 * @return a non empty array of {@link String}s containing not <code>null</code> elements with the possible URI
	 *         namespaces of the searched {@link Module}s. The elements in the array are <strong>ordered by
	 *         priority</strong>.
	 */
	protected abstract String[] getModuleNamespaces();

	protected abstract Collection<D> processModuleInternal(final M rssModule);
}
